#include "gy906.h"

#define GY906_PORT	GPIOB      //PB端口(端口和下面的两个针脚可自定义)
#define GY906_SCK		GPIO_PIN_9 //PB9：SCL
#define GY906_SDA		GPIO_PIN_8 //PB8：SDA

#define I2C_READ() HAL_GPIO_ReadPin(GY906_PORT,GY906_SDA)

static void SDA_IN(void) 
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
	/*Configure GPIO pin : PA6 */
  GPIO_InitStruct.Pin = GY906_SDA;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GY906_PORT, &GPIO_InitStruct);
}

static void SDA_OUT(void) 
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /*Configure GPIO pin : PA6 */
  GPIO_InitStruct.Pin = GY906_SDA;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GY906_PORT, &GPIO_InitStruct);
}

void I2C_SCL(int enable)
{
	if (1 == enable)
	{
		HAL_GPIO_WritePin(GY906_PORT, GY906_SCK, GPIO_PIN_SET);
	}else
	{
		HAL_GPIO_WritePin(GY906_PORT, GY906_SCK, GPIO_PIN_RESET);
	}
}

void I2C_SDA(int enable)
{
	if (1 == enable)
	{
		HAL_GPIO_WritePin(GY906_PORT, GY906_SDA, GPIO_PIN_SET);
	}else
	{
		HAL_GPIO_WritePin(GY906_PORT, GY906_SDA, GPIO_PIN_RESET);
	}
}

/*--------------------------------------------------------------------------------  
//  函数名称：void I2C_Delay()
//  函数功能：IIC短暂的软件延时
//	说明：对于标准IIC设备，最短延时5.425us，对于最新的IIC设备，或许可以缩短此延时
-----------------------------------------------------------------------------------*/
static void I2C_Delay() {
    delay_us(5);
}
//--------------------------------------------------------------------------------------   
//  函数名称：void I2C_Start() 
//  函数功能：IIC开始信号  
//--------------------------------------------------------------------------------------   
static void I2C_Start(void) {
	SDA_OUT();    		 //SDA线设置为输出
	I2C_SDA(1);  	  
	I2C_SCL(1); 
	I2C_Delay();
	
	I2C_SDA(0); 		 //产生开始信号
	I2C_Delay();	
	I2C_SCL(0);
    I2C_Delay();
}
//--------------------------------------------------------------------------------------   
//  函数名称：void I2C_Stop()
//  函数功能：IIC停止信号  
//--------------------------------------------------------------------------------------   
static void I2C_Stop(void){
	SDA_OUT();		 //sda线输出
	I2C_SCL(0);
	I2C_SDA(0); 	 //STOP:when CLK is high DATA change form low to high
	I2C_Delay();
	I2C_SCL(1);
	I2C_SDA(1);      //发送I2C总线结束信号
	I2C_Delay();
}
//--------------------------------------------------------------------------------------   
//  函数名称：uint8_t I2C_Get_Ack_From_Slave()
//  函数功能：IIC等待从设备的ACK信号
//	说明：这里使用主从通信协议，CPU是主设备 
//--------------------------------------------------------------------------------------   
static uint8_t I2C_Wait_Ack(){
	uint8_t ucErrTime = 0;
	
	SDA_IN();      			// SDA设置为输入  
	I2C_SDA(1);
	I2C_Delay();	   
	I2C_SCL(1);
	I2C_Delay();	 
	while(I2C_READ())
	{
		ucErrTime++;
		if(ucErrTime > 250)
		{
			I2C_Stop();
			return 1;
		}
	}
	I2C_SCL(0);				//时钟输出0
	return 0;
}
//--------------------------------------------------------------------------------------   
//  函数名称：uint8_t I2C_Write_Byte(unsigned char Data)
//  函数功能：IIC向从设备写一个字节的数据
//--------------------------------------------------------------------------------------   
static void I2C_Write_Byte(uint8_t txd){
	SDA_OUT(); 	  
	I2C_SCL(0);				        //拉低时钟开始数据传输
	for(uint8_t t = 0; t < 8; t++)  //开始准备信号线
	{
		if((txd&0x80) >> 7)
			I2C_SDA(1);
		else
			I2C_SDA(0);
		txd <<= 1; 	  
		I2C_Delay();  				//这三个延时都是必须的
		I2C_SCL(1);
		I2C_Delay(); 
		I2C_SCL(0);
		I2C_Delay();
	}
}

//产生ACK应答
static void IIC_Ack(void) {
	I2C_SCL(0);
	SDA_OUT();
	I2C_SDA(0);
	I2C_Delay();
	I2C_SCL(1);
	I2C_Delay();
	I2C_SCL(0);
}
// 不产生ACK应答		    
static void IIC_NAck(void){
	I2C_SCL(0);
	SDA_OUT();
	I2C_SDA(1);
	I2C_Delay();
	I2C_SCL(1);
	I2C_Delay();
	I2C_SCL(0);
}
//--------------------------------------------------------------------------------------   
//  函数名称：uint8_t I2C_Read_Byte(unsigned char* recv)
//  函数功能：IIC从从设备读一个字节的数据
//--------------------------------------------------------------------------------------   
static uint8_t I2C_Read_Byte(void) {
	uint8_t i = 0,receive = 0;
	
	SDA_IN();					 // SDA设置为输入
	for(i = 0;i < 8;i++ )
	{
		I2C_SCL(0); 
		I2C_Delay();
		I2C_SCL(1);
		receive <<= 1;
		if(I2C_READ())
			receive++;   
		I2C_Delay(); 
	}					 
//	if (!ack)
//		I2C_NAck();				 // 发送nACK
//	else
//		I2C_Ack(); 				 // 发送ACK   
	return receive;
}
/*-------------------------------------结束------------------------------------------*/   

#define Slave_WR               0
#define Slave_RD               1

uint8_t GY906_BAA_Init(void) { 
	// SCL / SDA	
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	__HAL_RCC_GPIOB_CLK_ENABLE();
	
  /*Configure GPIO pin : PB6 PB7*/
  GPIO_InitStruct.Pin = GY906_SCK|GY906_SDA;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GY906_PORT, &GPIO_InitStruct);
		
	I2C_SCL(1); 
	I2C_SDA(1);
	I2C_Delay();
	I2C_Delay(); 

	// 判断设备存在
	I2C_Start();
    I2C_Write_Byte(GY906_SlaveAddress);
	if(I2C_Wait_Ack())
    {
		rt_kprintf("I2C NO Ack\r\n");
        I2C_Stop();
        I2C_Delay(); I2C_Delay();
        return 1;
    }
	I2C_Stop();
	I2C_Delay(); I2C_Delay();

	return 0;
}

/*******************************************************************************
* Function Name  : PEC_calculation
* Description    : Calculates the PEC of received bytes
* Input          : pec[]
* Output         : None
* Return         : pec[0]-this byte contains calculated crc value
*******************************************************************************/
uint8_t PEC_Calculation(uint8_t pec[]) {
    uint8_t        crc[6];
    uint8_t        BitPosition=47;
    uint8_t        shift;
    uint8_t        i;
    uint8_t        j;
    uint8_t        temp;	
    do
    {
        /*Load pattern value 0x000000000107*/
        crc[5]=0;
        crc[4]=0;
        crc[3]=0;
        crc[2]=0;
        crc[1]=0x01;
        crc[0]=0x07;
        /*Set maximum bit position at 47 ( six bytes byte5...byte0,MSbit=47)*/
        BitPosition=47;
        /*Set shift position at 0*/
        shift=0;
        /*Find first "1" in the transmited message beginning from the MSByte byte5*/
        i=5;
        j=0;
        while((pec[i]&(0x80>>j))==0 && i>0)
        {
            BitPosition--;
            if(j<7)
            {
                j++;
            }
            else
            {
                j=0x00;
                i--;
            }
        }/*End of while */


        /*Get shift value for pattern value*/
        shift=BitPosition-8;
        /*Shift pattern value */
        while(shift)
        {
            for(i=5; i<0xFF; i--)
            {
                if((crc[i-1]&0x80) && (i>0))
                {
                    temp=1;
                }
                else
                {
                    temp=0;
                }
                crc[i]<<=1;
                crc[i]+=temp;
            }/*End of for*/
            shift--;
        }/*End of while*/
        /*Exclusive OR between pec and crc*/
        for(i=0; i<=5; i++)
        {
            pec[i] ^=crc[i];
        }/*End of for*/
    }
    while(BitPosition>8); /*End of do-while*/
    return pec[0];
}

/***************************************
    向I2C设备写入一个字节数据
****************************************/
uint8_t GY906_BAA_Single_Write(uint8_t cmd,uint16_t data,uint8_t pec) {

    // 起始信号
    I2C_Start();   

    // 从机地址
    I2C_Write_Byte((GY906_SlaveAddress | Slave_WR));
	if(I2C_Wait_Ack())
    {
        I2C_Stop();
        return 1;
    }
    
    // 寄存器地址
    I2C_Write_Byte(cmd);
	if(I2C_Wait_Ack())
    {
        I2C_Stop();
        return 1;
    }
    
    // 设备数据
    I2C_Write_Byte((uint8_t)(data>>8));
	if(I2C_Wait_Ack())
    {
        I2C_Stop();
        return 1;
    }
	I2C_Write_Byte((uint8_t)(data>>0));
	if(I2C_Wait_Ack())
    {
        I2C_Stop();
        return 1;
    } 
	I2C_Write_Byte(pec);
	if(I2C_Wait_Ack())
    {
        I2C_Stop();
        return 1;
    }  	
    // 结束信号
    I2C_Stop();
    return 0;
}

//***************************************
//    从I2C设备读取一个字节数据
//***************************************
uint16_t GY906_BAA_Single_Read(uint8_t cmd) {
    uint16_t data = 0;
	uint8_t  pec = 0;
	
	uint8_t DataL=0;             // Low data byte storage
    uint8_t DataH=0;             // High data byte storage    
	uint8_t arr[6] = {0};
	uint8_t PecReg;
	
	do{
		// 起始信号
		I2C_Start();			
		// 从机地址
		I2C_Write_Byte((GY906_SlaveAddress | Slave_WR));
		if(I2C_Wait_Ack())
		{
			I2C_Stop();
			return 1;
		}

		// 寄存器地址
		I2C_Write_Byte(cmd);
		if(I2C_Wait_Ack())
		{
			I2C_Stop();
			return 1;
		}
		
		// 起始信号
		I2C_Start();
		
		// 从机地址
		I2C_Write_Byte((GY906_SlaveAddress | Slave_RD));
		if(I2C_Wait_Ack())
		{
			I2C_Stop();
			return 1;
		}
		
		// 读出数据
		DataL = I2C_Read_Byte(); // 应答
		IIC_Ack();
		DataH = I2C_Read_Byte(); // 应答
		IIC_Ack();
		
		// PEC
		pec   = I2C_Read_Byte(); // 应答
		IIC_NAck();
		
		arr[5] = GY906_SlaveAddress;        
		arr[4] = cmd;
		arr[3] = GY906_SlaveAddress + 1;         //Load array arr
		arr[2] = DataL;                 
		arr[1] = DataH;                
		arr[0] = 0;                   
		PecReg = PEC_Calculation(arr);     //Calculate CRC
		
		// 结束信号   
		I2C_Stop();
	}while(PecReg != pec);
	
    data = (uint16_t)(DataH<<8) + (uint16_t)(DataL<<0);
	return data;
}





